home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ohlfind.zip / FIND.C < prev    next >
C/C++ Source or Header  |  1990-07-03  |  10KB  |  336 lines

  1. /* find -- search for files in a directory heirarchy
  2.    Copyright (C) 1987, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* GNU find was written by Eric Decker (cire@cisco.com),
  19.    with enhancements by David MacKenzie (djm@ai.mit.edu). */
  20.  
  21. /* Usage: find path... [expression]
  22.  
  23.    Predicates:
  24.  
  25.    Numbers can be specified as
  26.    +n for greater than n, or
  27.    -n for less than n,
  28.    n for exactly n.
  29.  
  30.    If none of -print, -ls, -ok, -exec are given, -print is assumed.
  31.  
  32.    -atime n            file last accessed n*24 hours ago
  33.    -ctime n            file status last modified n*24 hours ago
  34.    -depth            true; process dir contents before dir itself
  35.    -exec cmd            exec cmd, true if 0 status returned
  36.    -fulldays            true; from day boundaries rather than from now
  37.    -fstype type            file is on a filesystem of type type
  38.    -group gname            file belongs to group gname (gid allowed)
  39.    -inum n            file has inode number n
  40.    -links n            file has n links
  41.    -ls                true; list current file in 'ls -li' format
  42.    -mtime n            file data last modified n*24 hours ago
  43.    -name pattern        base of path name matches glob pattern
  44.                 ('*' and '?' do not match '.' at start)
  45.    -newer file            modtime is more recent than file's
  46.    -nouser            no user corresponds to file's uid
  47.    -nogroup            no group corresponds to file's gid
  48.    -ok cmd            like exec but ask user first; false if not 'y'
  49.    -perm mode            perm bits are exactly mode (octal or symbol)
  50.    -perm -mode            perm bits mode are set (s,s,t checked)
  51.    -permmask mode        true; set significant bits mask for next -perm
  52.                 (allows testing for unset bits)
  53.    -print            true; print current full pathname
  54.    -prune            (no -depth) true; do not descend current dir
  55.                 (-depth) false; no effect
  56.    -regex pattern        path name matches regex pattern
  57.    -size n[c]            file has n blocks (or chars)
  58.    -type c            file type: b, c, d, p, f, l, s
  59.    -user uname            file is owned by uname (uid allowed)
  60.    -version            true; print find version number on stderr
  61.    -xdev            true; don't descend dirs with different st_dev
  62.  
  63.    Grouping operators (in order of decreasing precendence):
  64.  
  65.    ( expr )            force precedence
  66.    ! expr            true if expr is false
  67.    expr1 expr2            and (implied); expr2 not eval if expr1 false
  68.    expr1 -o expr2        or; expr2 not eval if expr1 true
  69.    expr1 -or expr2        same as -o
  70.  
  71.    find processes files by applying each predicate in turn until the
  72.    overall expression evaluates false.  The expression evaluation
  73.    continues until the outcome is known (left hand side false for and,
  74.    true for or).  Once this happens no more expressions are
  75.    evaluated and find moves on to the next pathname.
  76.  
  77.    Exits with status 0 if all files are processed successfully,
  78.    >0 if error occurrs. */
  79.  
  80. #include <sys/types.h>
  81. #include <stdio.h>
  82. #ifndef USG
  83. #include <strings.h>
  84. #else
  85. #include <string.h>
  86. #define index strchr
  87. #define rindex strrchr
  88. #endif
  89. #include <sys/stat.h>
  90. #ifndef S_IFLNK
  91. #define lstat stat
  92. #endif
  93.  
  94. #include "defs.h"
  95.  
  96. #define apply_predicate(pathname, stat_buf_ptr, node)    \
  97.   (*(node)->pred_func)((pathname), (stat_buf_ptr), (node))
  98.  
  99. char *savedir ();
  100. void error ();
  101.  
  102. /* Name this program was run with. */
  103. char *program_name;
  104.  
  105. /* All predicates for each path to process. */
  106. struct pred_struct *predicates;
  107.  
  108. /* The last predicate allocated. */
  109. struct pred_struct *last_pred;
  110.  
  111. /* The root of the evaluation tree. */
  112. struct pred_struct *eval_tree;
  113.  
  114. /* If true, process directory before contents.  True unless -depth given. */
  115. boolean do_dir_first;
  116.  
  117. /* Global permission mask for -perm. */
  118. unsigned long perm_mask;
  119.  
  120. /* Seconds between 00:00 1/1/70 and either one day before now
  121.    (the default), or the start of today (if -fulldays is given). */
  122. long cur_day_start;
  123.  
  124. /* If true, cur_day_start has been adjusted to the start of the day. */
  125. boolean full_days;
  126.  
  127. /* If true, don't cross filesystem boundaries. */
  128. boolean stay_on_filesystem;
  129.  
  130. /* If true, don't descend past current directory. */
  131. boolean stop_at_current_level;
  132.  
  133. /* Status value to return to system. */
  134. int exit_status;
  135.  
  136. void
  137. main (argc, argv)
  138.      int argc;
  139.      char *argv[];
  140. {
  141.   int i;
  142.   PFB parse_function;        /* Pointer to who is to do the parsing. */
  143.   struct pred_struct *cur_pred;
  144.   char *predicate_name;        /* Name of predicate being parsed. */
  145.  
  146.   program_name = argv[0];
  147.  
  148.   predicates = NULL;
  149.   last_pred = NULL;
  150.   do_dir_first = true;
  151.   cur_day_start = time ((long *) 0) - DAYSECS;
  152.   full_days = false;
  153.   stay_on_filesystem = false;
  154.   exit_status = 0;
  155.  
  156. #ifdef    DEBUG
  157.   printf ("%ld %s", cur_day_start, ctime (&cur_day_start));
  158. #endif /* DEBUG */
  159.  
  160.   /* Find where in ARGV the predicates begin. */
  161.   for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
  162.     ;
  163.  
  164.   if (i == 1)
  165.     usage ("no paths specified");
  166.  
  167.   /* Enclose the expression in `( ... )' so a default -print will
  168.      apply to the whole expression. */
  169.   parse_open (argv, &argc);
  170.   /* Build the input order list. */
  171.   while (i < argc)
  172.     {
  173.       if (index ("-!()", argv[i][0]) == 0)
  174.     usage ("paths must precede expression");
  175.       predicate_name = argv[i];
  176.       parse_function = find_parser (predicate_name);
  177.       if (parse_function == NULL)
  178.     error (1, 0, "invalid predicate `%s'", predicate_name);
  179.       i++;
  180.       if (!(*parse_function) (argv, &i))
  181.     {
  182.       if (argv[i] == NULL)
  183.         error (1, 0, "missing argument to `%s'", predicate_name);
  184.       else
  185.         error (1, 0, "invalid argument to `%s'", predicate_name);
  186.     }
  187.     }
  188.   if (predicates->pred_next == NULL)
  189.     {
  190.       /* No predicates that are entered into the tree were given.
  191.      Remove the `(', because `( ) -print' is not a valid expression. */
  192.       free (predicates);
  193.       predicates = last_pred = NULL;
  194.     }
  195.   else
  196.     parse_close (argv, &argc);
  197.  
  198.   if (no_side_effects (predicates))
  199.     parse_print (argv, &argc);
  200.  
  201. #ifdef    DEBUG
  202.   print_list (predicates);
  203. #endif /* DEBUG */
  204.  
  205.   /* Done parsing the predicates.  Build the evaluation tree. */
  206.   cur_pred = predicates;
  207.   eval_tree = get_expr (&cur_pred, NO_PREC);
  208. #ifdef    DEBUG
  209.   print_tree (eval_tree, 0);
  210. #endif /* DEBUG */
  211.  
  212.   /* Process all of the input paths. */
  213.   for (i = 1; i < argc && index ("-!()", argv[i][0]) == 0; i++)
  214.     process_path (argv[i], true);
  215.  
  216.   exit (exit_status);
  217. }
  218.  
  219. /* Recursively descend path PATHNAME, applying the predicates.
  220.    If ROOT is true, PATHNAME is a command line argument, and
  221.    thus the root of a subtree. */
  222.  
  223. void
  224. process_path (pathname, root)
  225.      char *pathname;
  226.      boolean root;
  227. {
  228.   static dev_t root_dev;
  229.   struct stat stat_buf;
  230.   char *name_space;
  231.  
  232.   if (lstat (pathname, &stat_buf) != 0)
  233.     {
  234.       error (0, errno, "%s", pathname);
  235.       exit_status = 1;
  236.       return;
  237.     }
  238.  
  239.   if ((stat_buf.st_mode & S_IFMT) != S_IFDIR)
  240.     {
  241.       perm_mask = 07777;    /* Start fresh. */
  242.       apply_predicate (pathname, &stat_buf, eval_tree);
  243.       return;
  244.     }
  245.  
  246.   if (stay_on_filesystem)
  247.     {
  248.       if (root)
  249.     root_dev = stat_buf.st_dev;
  250.       else if (stat_buf.st_dev != root_dev)
  251.     return;
  252.     }
  253.  
  254.   stop_at_current_level = false;
  255.  
  256.   if (do_dir_first)
  257.     {
  258.       perm_mask = 07777;    /* Start fresh. */
  259.       apply_predicate (pathname, &stat_buf, eval_tree);
  260.     }
  261.  
  262.   if (stop_at_current_level == false)
  263.     {
  264.       errno = 0;
  265.       name_space = savedir (pathname, stat_buf.st_size);
  266.       if (name_space == NULL)
  267.     {
  268.       if (errno)
  269.         {
  270.           error (0, errno, "%s", pathname);
  271.           exit_status = 1;
  272.         }
  273.       else
  274.         error (1, 0, "virtual memory exhausted");
  275.     }
  276.       else
  277.     {
  278.       char *namep;        /* Current point in `name_space'. */
  279.       char *cur_path;    /* Full path of each file to process. */
  280.       unsigned cur_path_size; /* Bytes allocated for `cur_path'. */
  281.       unsigned file_len;    /* Length of each path to process. */
  282.       unsigned pathname_len; /* Length of `pathname' + 2. */
  283.  
  284.       if (!strcmp (pathname, "/"))
  285.         pathname_len = 2;    /* Won't add a slash to this. */
  286.       else
  287.         pathname_len = strlen (pathname) + 2; /* For '/' and '\0'. */
  288.       cur_path_size = 0;
  289.       cur_path = NULL;
  290.  
  291.       for (namep = name_space; *namep;
  292.            namep += file_len - pathname_len + 1)
  293.         {
  294.           file_len = pathname_len + strlen (namep);
  295.           if (file_len > cur_path_size)
  296.         {
  297.           while (file_len > cur_path_size)
  298.             cur_path_size += 1024;
  299.           if (cur_path)
  300.             free (cur_path);
  301.           cur_path = xmalloc (cur_path_size);
  302.           strcpy (cur_path, pathname);
  303.           cur_path[pathname_len - 2] = '/';
  304.         }
  305.           strcpy (cur_path + pathname_len - 1, namep);
  306.           process_path (cur_path, false);
  307.         }
  308.       if (cur_path)
  309.         free (cur_path);
  310.       free (name_space);
  311.     }
  312.     }
  313.  
  314.   if (do_dir_first == false)
  315.     {
  316.       perm_mask = 07777;    /* Start fresh. */
  317.       apply_predicate (pathname, &stat_buf, eval_tree);
  318.     }
  319. }
  320.  
  321. /* Return true if there are no side effects in any of the predicates in
  322.    predicate list PRED, false if there are any. */
  323.  
  324. boolean
  325. no_side_effects (pred)
  326.      struct pred_struct *pred;
  327. {
  328.   while (pred != NULL)
  329.     {
  330.       if (pred->side_effects)
  331.     return (false);
  332.       pred = pred->pred_next;
  333.     }
  334.   return (true);
  335. }
  336.